#include "api.h"
#include "http.h"
#include "standarditemmodel.h"
#include <QtCore/QDateTime>
#include <QtCore/QCryptographicHash>
#include <QtCore/QFile>
#include <QtCore/QDataStream>
#include <QtCore/QByteArray>
#include <QtCore/QUuid>
#include <QtNetwork/QHostInfo>
//#include <QtCore/5.4.0/QtCore/private/qversionnumber_p.h>
#include <QtConcurrent/QtConcurrent>
#include <QtNetwork/QNetworkInterface>
#include <QtGui>

Api * __Api = Q_NULLPTR;

QObject *Api_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    // auto deleted
    __Api = new Api;

    return __Api;
}


Api::Api(QObject *parent)
    : QObject(parent)
{

}

Api::~Api()
{

}

QString Api::sslRandom( int len )
{
    QByteArray data(len, '\0');

    //    RAND_set_rand_method(RAND_SSLeay());

    //    RAND_bytes((unsigned char *)data.data(), len / 2);

    //    RAND_cleanup();

    return data.toHex();
}

QString Api::newGUID()
{
    return QUuid::createUuid().toString().mid(1, 36);
}

bool Api::isValid(const QDateTime &dm)
{
    return dm.isValid();
}

bool Api::isValid(const QUrl &url)
{
    if (!url.isValid())
        return false;

    if (url.scheme().startsWith("http:"))
    {
        return true;
    }
    else if (url.scheme().startsWith("https:"))
    {
        return true;
    }
    else if (url.scheme().startsWith("file:"))
    {
        return true;
    }

    return false;
}

QDateTime Api::addTime(const QDateTime &dm, int n, const QString &f)
{
    QDateTime dtm = dm;

    if (f == "M" || f.toLower() == "month")
    {
        dtm = dm.addMonths(n);
    }
    else if (f == "d" || f.toLower() == "day")
    {
        dtm = dm.addDays(n);
    }
    else if (f == "s" || f.toLower() == "second")
    {
        dtm = dm.addSecs(n);
    }

    return dtm;
}

//QString Api::toLocalFile(const QUrl &fileUrl)
//{
//    if (fileUrl.isLocalFile()) {
//        return fileUrl.toLocalFile();
//    }
//    return fileUrl.toString();
//}

//QUrl Api::toUrl(const QString &fileName)
//{
//    return QUrl::fromLocalFile(fileName);
//}

QDateTime Api::lastModified(const QString &fileName)
{
    QFileInfo ifi(fileName);

    return ifi.lastModified();
}

QString Api::fileName(const QString &filePath)
{
    QFileInfo ifi(filePath);

    return ifi.completeBaseName();
}

int Api::fileSize(const QString fileName)
{
    QFileInfo info(fileName);

    return info.size();
}

//int Api::buildVersion()
//{
//    QVersionNumber vn = QVersionNumber::fromString(QCoreApplication::applicationVersion());

//    return vn.segments().last();
//}

QStringList Api::macAddress()
{
    QStringList macs;

    QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();

    QListIterator<QNetworkInterface> itor(interfaces);

    while (itor.hasNext())
    {
        QNetworkInterface i = itor.next();

        if (!i.isValid())
            continue;

        if (!i.flags().testFlag(QNetworkInterface::IsRunning))
            continue;

        if (i.flags().testFlag(QNetworkInterface::IsLoopBack))
            continue;

        if (i.flags().testFlag(QNetworkInterface::IsPointToPoint))
            continue;

        macs.append(i.hardwareAddress());
    }

    macs.sort(Qt::CaseInsensitive);

    return macs;
}

QString Api::folder(const QString &filePath) const
{
    QFileInfo fi(filePath);

    return fi.absolutePath();
}

QString Api::localHostName()
{
    return Api::LocalHostName();
}

QString Api::LocalHostName()
{
    return QHostInfo::localHostName();
}

QVariantMap Api::urlInfo(const QUrl &url)
{
    QVariantMap map;

    map.insert("fileName", url.fileName());
    map.insert("host", url.host());
    map.insert("userName", url.host());
    return map;
}

QVariantMap Api::mailboxInfo(const QString &mb)
{
    QStringList list = mb.split("@");

    QVariantMap map;

    map.insert("userName", list.first());
    map.insert("domain", list.last());

    return map;
}

qint64 Api::diffTime(const QDateTime &d1, const QDateTime &d2, const QString &f)
{
    int result = 0;
    if (f.toLower() == "y" || f.toLower() == "year") {
        result = d1.date().year() - d2.date().year();
    }
    else if (f == "M" || f.toLower() == "month") {
        int max = qMax(d1.date().year(), d2.date().year());

        //        for (int i = d1.date().year(); i < d2.date().year())
        //        result = (d1.date().year() - d2.date().year()) * 12 + d1.date().month() + 12 - d2.date().month();
    }
    else if (f == "d" || f.toLower() == "day")
    {
        result = d1.daysTo(d2);
    }
    else if (f == "s" || f.toLower() == "second")
    {
        result = d1.secsTo(d2);
    }

    return result;
}

bool Api::IsValidImage(const QString &imageData)
{
    QRegularExpressionMatch match;
    int index = imageData.indexOf(QRegularExpression("data:image/.+;base64,(.+)"), 0, &match);

    if (index == -1)
        return false;

    QString base64 = match.captured(1);

    QPixmap pixmap;

    pixmap.loadFromData(QByteArray::fromBase64(base64.toUtf8()));

    return !pixmap.isNull();
}

void Api::toFileImage(const QString &fileName, const QString &data)
{
    //QDir writable = this->location("data").toString();

    //QString url = writable.absoluteFilePath(fileName);

    QSaveFile file(fileName);

    file.open(QIODevice::WriteOnly | QIODevice::Truncate);

    file.write(QByteArray::fromBase64(data.toLocal8Bit()));

    file.commit();

    //return QUrl::fromLocalFile(url);
}

void Api::updateVersion(const QString &fileName)
{
    qApp->setProperty("startup", fileName);

    qApp->quit();
}

void Api::restart(const QStringList &arguments)
{
    QProcess::startDetached(QDir::toNativeSeparators(QCoreApplication::applicationFilePath()), arguments);
    qApp->quit();
}

QString Api::encrypt(const QString &key, const QString &data)
{
    QByteArray ikey =QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Md5);

    QByteArray result = data.toUtf8().toBase64();

    std::reverse(result.begin(), result.end());
    std::reverse(ikey.begin(), ikey.end());

    int index = 0;

    for (QByteArray::iterator itor = result.begin(); itor !=result.end(); itor++ ) {
        if (index == ikey.length()) {
            index = 0;
        }
        QChar x = ikey.at(index);

        *itor = QChar(*itor).cell() ^ x.cell();

        index++;
    }

    return result.toBase64();
}

QString Api::decrypt(const QString &key, const QString &data)
{
    QByteArray ikey =QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Md5);

    std::reverse(ikey.begin(), ikey.end());

    QByteArray result =  QByteArray::fromBase64(data.toUtf8());

    int index = 0;

    for (QByteArray::iterator itor = result.begin(); itor !=result.end(); itor++ ) {
        if (index == ikey.length()) {
            index = 0;
        }
        QChar x = ikey.at(index);

        *itor = QChar(*itor).cell() ^ x.cell();

        index++;
    }

    std::reverse(result.begin(), result.end());

    return QString::fromUtf8(QByteArray::fromBase64(result));
}

QString Api::HexMD5(const QString &data)
{
    return QCryptographicHash::hash(data.toUtf8(), QCryptographicHash::Md5).toHex();
}

//QString Api::desEncrypt( const QString &key, const QString &data )
//{
//    QByteArray d = ::encrypt(EVP_des_cbc(), key, data.toUtf8().toHex());

//    return d.toHex();
//}

//QString Api::desDecrypt( const QString &key, const QString &data )
//{
//    QByteArray d = ::decrypt(EVP_des_cbc(), key, QByteArray::fromHex(data.toLocal8Bit()));

//    return QByteArray::fromHex(d);
//}

//QString Api::aesEncrypt( const QString &key, const QString &data )
//{
//    QByteArray d = ::encrypt(EVP_aes_128_cbc(), key, data.toUtf8());

//    return d.toHex();
//}

//QString Api::aesDecrypt( const QString &key, const QString &data )
//{
//    QByteArray d = ::decrypt(EVP_aes_128_cbc(), key, QByteArray::fromHex(data.toLocal8Bit()));
//    return QString::fromUtf8(d);
//}

QString Api::__newGUID()
{
    return Api::newGUID();
}

QString Api::fragment(const QUrl &url, QUrl::ComponentFormattingOptions options) const
{
    return url.fragment(options);
}

QUrl Api::setFragment(const QUrl &url, const QString &fragment, QUrl::ParsingMode mode)
{
    QUrl ret = url;
    ret.setFragment(fragment, mode);
    return ret;
}

QUrl Api::addQuery(const QUrl &url, const QVariantMap &data)
{
    QUrl ret = url;

    QUrlQuery query(url.query());

    QMapIterator<QString, QVariant> itor(data);

    while (itor.hasNext()) {
        itor.next();
        query.addQueryItem(itor.key(), itor.value().toString());
    }

    ret.setQuery(query.toString(QUrl::FullyEncoded));

    return ret;
}

bool Api::hasQuery(const QUrl &url) const
{
    return url.hasQuery();
}

QString Api::toPercentEncoding(const QString &keyword)
{
    return keyword.toLocal8Bit().toPercentEncoding();
}

QVariantMap Api::query(const QUrl &url) const
{
    QUrlQuery query(url.query());

    QList<QPair<QString, QString> > list = query.queryItems();

    QVariantMap ret;

    for (int i = 0; i < list.count(); ++i) {
        QPair<QString, QString> p = list.at(i);
        ret.insert(p.first, p.second);
    }

    return ret;
}

QString Api::toQueryString(const QVariantMap &d)
{
    QUrlQuery query;

    QMapIterator<QString, QVariant> itor(d);

    while (itor.hasNext()) {
        itor.next();
        query.addQueryItem(itor.key(), itor.value().toString());
    }

    return query.toString(QUrl::FullyEncoded);
}

QVariantMap Api::iniData(const QString &fileName)
{
    QSettings settings(fileName, QSettings::IniFormat);
    settings.setIniCodec("utf-8");

    QStringList list = settings.childGroups();
    QVariantMap data;

    Q_FOREACH(QString g, list)
    {
        QVariantMap child;

        settings.beginGroup(g);

        Q_FOREACH(QString k, settings.allKeys())
        {
            child[k] = settings.value(k);
        }

        settings.endGroup();

        data[g] = child;
    }

    return data;
}

QStringList Api::filterFiles(const QString &path, const QStringList &filter)
{
    QDir dir(path);

    return dir.entryList(filter, QDir::Files, QDir::Name);
}

void Api::keyReturnEvent(QQuickItem *item)
{
    QObject *object = item;
    if (object) {
        QKeyEvent * keyp = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);

        QCoreApplication::postEvent(object, keyp);

        QKeyEvent * keyr = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier);

        QCoreApplication::postEvent(object, keyr);
    }

}

void Api::mouseClickEvent(QQuickItem *item, const QPoint &pos)
{
    QMouseEvent * mousep = new QMouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

    QCoreApplication::postEvent(item, mousep);

    QMouseEvent * mousepr = new QMouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

    QCoreApplication::postEvent(item, mousepr);

}

void Api::keyDownEvent(QQuickItem *item)
{

    QObject *object = item;
    if (object) {
        QKeyEvent * keyp = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);

        QCoreApplication::postEvent(object, keyp);

        QKeyEvent * keyr = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier);

        QCoreApplication::postEvent(object, keyr);
    }

}

int Api::length(const QString &data)
{
    return data.trimmed().toLocal8Bit().length();
}

QString Api::openFolderDialog(const QString &caption, const QString &dir)
{
    //return QFileDialog::getExistingDirectoryUrl(0, caption, dir).toString();
    return "";
}

QString Api::grabScreen(int x, int y, const QSize &size)
{
    QPixmap screen = qApp->primaryScreen()->grabWindow(0);

    QPixmap pixmap = screen.copy(x, y, size.width(), size.height());

    QByteArray bytes;
    QBuffer buffer(&bytes);

    buffer.open(QIODevice::WriteOnly);

    pixmap.save(&buffer, "PNG");

    return bytes.toBase64();
    //qApp->clipboard()->setPixmap();
}

QString Api::grabWId(uint hWnd, int x, int y, const QSize &size)
{
    QPixmap pixmap = qApp->primaryScreen()->grabWindow(hWnd, x, y, size.width(), size.height());

    QByteArray bytes;
    QBuffer buffer(&bytes);

    buffer.open(QIODevice::WriteOnly);

    pixmap.save(&buffer, "PNG");

    return bytes.toBase64();
}

QString Api::combinePixmap(const QStringList &pixmapData)
{
    QLinkedList<QPixmap> pixmaps;

    int width = 0;
    int height = 0;

    Q_FOREACH (QString px, pixmapData)
    {
        QPixmap pixmap;

        pixmap.loadFromData(QByteArray::fromBase64(px.toLocal8Bit()));

        width = qMax(width, pixmap.width());

        height += pixmap.height();

        pixmaps << pixmap;
    }

    QPixmap pixmap(width, height);

    QLinkedListIterator<QPixmap> itor(pixmaps);

    QPainter p(&pixmap);

    int y = 0;

    while (itor.hasNext())
    {
        const QPixmap &pixmap = itor.next();

        p.drawPixmap(0, y, pixmap);
        y += pixmap.height();
    }

    p.end();

    QByteArray bytes;
    QBuffer buffer(&bytes);

    buffer.open(QIODevice::WriteOnly);

    pixmap.save(&buffer, "PNG");

    return bytes.toBase64();
}

QString Api::zeroFill(qlonglong num, int fieldWidth)
{
    QString str = "%1";
    return str.arg(num, fieldWidth, 10, QChar('0'));
}

void Api::closeTopWindow()
{
    QQuickWindow *w =  qobject_cast<QQuickWindow*>(qApp->topLevelWindows().at(0));

    if (w) {
        w->close();
    }
}

//QString Api::writableLocation(int index) const
//{
//    // android: /data/data/qt.awfan.jkzs/files
//    QString wpath = QStandardPaths::writableLocation(static_cast<QStandardPaths::StandardLocation>(index));

//    return wpath;
//}

void Api::explorer(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

//bool Api::mkpath(const QString &path)
//{
//    QString dir = path;
//    QUrl url(path);
//    if (url.isLocalFile()) {
//        dir = url.toLocalFile();
//    }
//    return QDir(QCoreApplication::applicationDirPath()).mkpath(dir);
//}

QString Api::toBase64(const QString &filePath, const QSize &size)
{
    QByteArray data;

    {
        QPixmap pixmap(filePath);

        qDebug() << "pixmap path: " << filePath;

        if (!pixmap.isNull())
        {
            if (size.isValid())
            {
                pixmap = pixmap.scaled(size);
            }

            QBuffer buffer(&data);

            buffer.open(QIODevice::WriteOnly);

            pixmap.save(&buffer, "PNG");
        }
    }

    //"data:image/png;base64," +
    return data.toBase64();
}

QDateTime Api::toDateTime(const QString &datetime, const QString &format)
{
    return QDateTime::fromString(datetime, format);
}

QPoint Api::mapToGlobal(QWindow *window, const QPoint &pos) const
{
    if (window) {
        return window->mapToGlobal(pos);
    }

    return pos;
}

void Api::setCurror(const QPoint &p)
{
    QCursor::setPos(p);
}

bool Api::contains(const QRectF &rect, int x, int y)
{
    return rect.contains(x, y);
}

//QStringList Api::files(const QString &path)
//{
//    QDir dir(path);

//    return dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
//}

QString Api::grabRect(const QRect &rect)
{
    QQuickWindow *w =  qobject_cast<QQuickWindow*>(qApp->topLevelWindows().at(0));

    QImage window = w->grabWindow();

    int width = rect.width();

    if (width <= 0)
        width = window.width();

    int height = rect.height();

    if (height <= 0)
        height = window.height();

    QPixmap pixmap;

    pixmap.convertFromImage(window.copy(rect.x(), rect.y(), width, height));

    QByteArray bytes;
    QBuffer buffer(&bytes);

    buffer.open(QIODevice::WriteOnly);

    pixmap.save(&buffer, "PNG");

    return bytes.toBase64();

    //qApp->clipboard()->setImage(image);
}

//QString Api::patternOfEmail()
//{
//    return "^[\w\d._%+-]+@(?:[\w\d-]+\.)+(\w{2,})$";
//}

bool Api::hasFragment(const QUrl &url) const
{
    return url.hasFragment();
}

//QString Api::downloadLocation() const
//{
//    return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
//}

QStringList Api::readAllLine(const QString &fileName)
{
    QFile file(fileName);

    QStringList list;

    if (!file.open(QIODevice::ReadOnly))
        return list;

    QTextStream stream(&file);

    stream.setAutoDetectUnicode(true);

    QString line;

    do {
        line = stream.readLine();

        list.append(line);

    } while (!line.isNull());

    return list;
}

QString Api::stringify(const QVariantMap &json)
{
    QJsonDocument document(QJsonObject::fromVariantMap(json));
#ifdef QT_DEBUG
    qInfo() << ">>>>send stringify:" + document.toJson();
#endif
    return document.toJson();
}

QVariantMap Api::jsonParse(const QString &json)
{
#ifdef QT_DEBUG
    qInfo() << ">>>>receive json:" + json;
#endif
    QJsonDocument document = QJsonDocument::fromJson(json.toUtf8());
    return document.object().toVariantMap();
}



QString Api::workPath() const
{
    return QCoreApplication::applicationDirPath();
}

//bool Api::fileExist(const QString &fileName) const
//{
//    return QFile::exists(fileName);
//}

bool Api::pathExist(const QString &path) const
{
    //    QDir dir = QCoreApplication::applicationDirPath();

    //    return dir.exists(path);
    QFileInfo f(path);

    // qDebug() << f.exists();
    return f.exists();
}


bool Api::isImage(const QUrl &url) const
{
    QMimeType t = m_mime.mimeTypeForUrl(url);

    return (t.inherits("image/jpeg") || t.inherits("image/png"));
}

void Api::createTreeModel(const QString &name)
{
    ///m_engine->rootContext()->setContextProperty(name, new StandardItemModel(this));
}

QString Api::__fileMD5(const QString &fileName)
{
    return Api::fileMD5(fileName);
}

void Api::__textToClipboard(const QString &text)
{

}

void Api::__imageToClipboard(const QString &text)
{

}

QString Api::__imageFileBase64(const QString &fileName, const QSize &size)
{
    QString  prefix = "data:%1;base64,";

    QUrl url(fileName);

    QString filePath = url.isLocalFile() ? url.toLocalFile() : url.path();

    QFile file(QDir::toNativeSeparators(filePath));

    if (!file.open(QIODevice::ReadOnly)) {
        return "";
    }

    QByteArray byteArray = file.readAll();

    QMimeType t = m_mime.mimeTypeForData(byteArray);

    qDebug() <<"suffix:" << t.preferredSuffix() << " length:" << byteArray.length();

    QByteArray data;

    {

        QPixmap pixmap;
        pixmap.loadFromData(byteArray);

        if (!pixmap.isNull())
        {
            if (size.isValid())  {
                pixmap = pixmap.scaled(size);
            }

            QBuffer buffer(&data);

            buffer.open(QIODevice::WriteOnly);

            pixmap.save(&buffer, qPrintable(t.preferredSuffix().toUpper()));
        }
    }

    if (!Api::supportedImages().contains(t.name())) {
        return "";
    }

    qDebug() << "base64 length: " << data.toBase64().length();
    prefix = prefix.arg(t.name());

    return data.isEmpty() ? "" : prefix + data.toBase64();
}

bool Api::__isSupportedImage(const QString &fileName)
{
    QMimeType t = m_mime.mimeTypeForFile(fileName);

    QStringList list;
    list << "image/jpeg";
    list << "image/png";
    list << "image/gif";
    list << "image/bmp";

    foreach (QString image, list) {
        if (t.inherits(image))
            return true;
    }

    return false;
}

QString Api::favicon()
{
    QFile file(":/favicon.png");
    if (file.open(QIODevice::ReadOnly)) {
        QByteArray fav;

        fav = file.readAll();

        qDebug() << "fav length:" << fav.length();
        qDebug() << fav.toBase64().left(100);
        return fav.toBase64();
    }

    return "";
}

bool Api::isValidImage(const QString &imageData)
{
    return Api::IsValidImage(imageData);
}

QString Api::key(const QUrl &url)
{
    QUrl u = url.adjusted(QUrl::RemoveFilename);

    return u.path();

}

QString Api::fileMD5(const QString &fileName)
{
    QString md5;
    QFile file(fileName);
    if (file.open(QIODevice::ReadOnly))
        md5 = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5).toHex();

    return md5;
}

QHostAddress Api::activeHostAddress(/*QHostAddress::SpecialAddress special*/)
{
    QTcpSocket tcp;
    tcp.connectToHost(QCoreApplication::organizationDomain(), 80);

    qDebug() << "domain:" << QCoreApplication::organizationDomain();
    if (!tcp.waitForConnected())
    {
        return tcp.localAddress();
    }
    else
    {
        foreach (QHostAddress address, QNetworkInterface::allAddresses()) {
            qDebug() << "address:" << address;
            if (address.isLoopback()) {
                // qDebug() << "address:" << address;
                continue;
            }
            //if (address == special)
            return address;
        }
    }

    return QHostAddress::Null;
}

void Api::textToClipboard(const QString &text)
{
    QGuiApplication::clipboard()->setText(text);
}

void Api::imageToClipboard(const QString &text)
{
    QPixmap pixmap;
    pixmap.loadFromData(QByteArray::fromBase64(text.toUtf8()));
    QGuiApplication::clipboard()->setPixmap(pixmap);
}


QStringList Api::supportedImages()
{
    QStringList list;

    list << "image/jpeg";
    list << "image/png";
    list << "image/gif";
    list << "image/bmp";

    return list;
}

